#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#include <sys/select.h>  // 用于非阻塞检测按键
#include "MQTTClient.h"
#include "shmem.h"


#define BROKERADDRESS "tcp://d21ddf1880.st1.iotda-device.cn-north-4.myhuaweicloud.com:1883"
#define CLIENTID "68c7ce8232771f177b606477_ai999_0_0_2025091908"
#define QOS 1
#define TIMEOUT 10000L
#define USERNAME "68c7ce8232771f177b606477_ai999"
#define PASSWORD "a43233818adcda5ba991ceb1d5e45de2dc93cc57bf7accfea2e9fb463818d61b"
#define KEEP_ALIVE_INTERVAL 60 // 心跳间隔为60秒
#define PUBTOPIC "$oc/devices/68c7ce8232771f177b606477_ai999/sys/properties/report"
#define SUBTOPIC "$oc/devices/68c7ce8232771f177b606477_ai999/sys/messages/down"
#define UPLOAD_INTERVAL 2 // 数据上报间隔(秒)，可根据需要调整

#define MAX_NODE 128  //最大支持学生数目，实际可用是127
#define STD_NODE_LEN sizeof(struct shm_param)
#define MAX_NODE_SIZE (MAX_NODE * STD_NODE_LEN)

volatile MQTTClient_deliveryToken deliveredtoken;

// 共享内存数据结构定义（确保与写入端一致）
union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};

struct shmem_all
{
    char name[34];
    char ip[32];
    int prot;
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 最新数据
    int ret;             // 状态标识：-1失败，0成功
};

// 从MQTT连接字符串中提取端口号
int extract_port_from_address(const char *address)
{
    const char *colon_pos = strrchr(address, ':');
    if (colon_pos == NULL)
        return -1;

    char *end_ptr;
    long port = strtol(colon_pos + 1, &end_ptr, 10);

    if (end_ptr == colon_pos + 1 || *end_ptr != '\0')
        return -1;
    if (port < 1 || port > 65535)
        return -1;

    return (int)port;
}

// 从MQTT连接字符串中提取主机名
void extract_host_from_address(const char *address, char *host, size_t host_size)
{
    const char *start = address;
    if (strncmp(address, "tcp://", 6) == 0)
    {
        start = address + 6;
    }

    const char *colon_pos = strrchr(start, ':');
    if (colon_pos == NULL)
    {
        strncpy(host, start, host_size - 1);
        host[host_size - 1] = '\0';
        return;
    }

    size_t host_len = colon_pos - start;
    if (host_len >= host_size)
        host_len = host_size - 1;

    strncpy(host, start, host_len);
    host[host_len] = '\0';
}

// 设置终端为无需回车即可读取字符的模式
void set_terminal_mode()
{
    struct termios ttystate;
    tcgetattr(STDIN_FILENO, &ttystate);
    ttystate.c_lflag &= ~(ICANON | ECHO);
    ttystate.c_cc[VMIN] = 1;
    tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}

// 恢复终端模式
void reset_terminal_mode()
{
    struct termios ttystate;
    tcgetattr(STDIN_FILENO, &ttystate);
    ttystate.c_lflag |= (ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &ttystate);
}

// 非阻塞检查按键输入
int kbhit(void)
{
    struct timeval tv = {0L, 0L};
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(STDIN_FILENO, &fds);
    return select(1, &fds, NULL, NULL, &tv);
}

// 连接丢失回调函数
void connlost(void *context, char *cause)
{
    printf("\nConnection lost\n");
    printf("     cause: %s\n", cause);
    printf("Attempting to reconnect...\n");
}

// 消息到达回调函数
int msgarrvd(void *context, char *topicName, int topicLen, MQTTClient_message *message)
{
    printf("\n[Message received]\n");
    printf("  Topic: %s\n", topicName);
    printf("  Message: %.*s\n", message->payloadlen, (char *)message->payload);
    printf("  QoS: %d\n", message->qos);
    printf("  Retained: %s\n", message->retained ? "yes" : "no");
    MQTTClient_freeMessage(&message);
    MQTTClient_free(topicName);
    return 1;
}

// 交付完成回调函数
void delivered(void *context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
    deliveredtoken = dt;
}

// 显示连接信息
void print_connection_info(MQTTClient_connectOptions *conn_opts)
{
    char host[256];
    extract_host_from_address(BROKERADDRESS, host, sizeof(host));
    int port = extract_port_from_address(BROKERADDRESS);

    printf("\n==================== Connection Information ====================\n");
    printf("  Host: %s\n", host);
    printf("  Port: %d\n", port);
    printf("  Client ID: %s\n", CLIENTID);
    printf("  Username: %s\n", conn_opts->username);
    printf("  Keep Alive Interval: %d seconds\n", conn_opts->keepAliveInterval);
    printf("  Clean Session: %s\n", conn_opts->cleansession ? "yes" : "no");
    printf("  Publish Topic: %s\n", PUBTOPIC);
    printf("  Subscribe Topic: %s\n", SUBTOPIC);
    printf("  Data Upload Interval: %d seconds\n", UPLOAD_INTERVAL);
    printf("================================================================\n");
}

// 发布JSON格式的消息 //加模块的话直接在里边加
int publish_json_message(MQTTClient client, const char *topic, const char *service_id,
                        int beep, int ai, int fan, int temp, int humidity,
                        int light, int person_detect, int motor)
{
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;
    int rc;

    // 构建JSON消息
    char json_message[512];
    snprintf(json_message, sizeof(json_message),//在这里边也加模块
             "{\"services\": [{\"service_id\": \"%s\", \"properties\": "
             "{\"蜂鸣器\": %s, \"ai\": %s, \"排风扇\": %s, "
             "\"温度\": %d, \"湿度\": %d, \"灯\": %s, "
             "\"人员检测\": %s, \"马达\": %s}}]}",
             service_id,
             beep ? "true" : "false",
             ai ? "true" : "false",
             fan ? "true" : "false",
             temp,
             humidity,
             light ? "true" : "false",
             person_detect ? "true" : "false",
             motor ? "true" : "false");

    printf("Publishing message: %s\n", json_message);

    pubmsg.payload = json_message;
    pubmsg.payloadlen = (int)strlen(json_message);
    pubmsg.qos = QOS;
    pubmsg.retained = 0;

    rc = MQTTClient_publishMessage(client, topic, &pubmsg, &token);
    if (rc != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to publish message, return code %d\n", rc);
        return rc;
    }

    printf("Message published to topic '%s'\n", topic);
    printf("Waiting for delivery confirmation...\n");
    rc = MQTTClient_waitForCompletion(client, token, TIMEOUT);
    if (rc != MQTTCLIENT_SUCCESS)
    {
        printf("Delivery failed, return code %d\n", rc);
    }
    else
    {
        printf("Message delivered successfully\n");
    }

    return rc;
}

// 从共享内存读取最新数据  加
void read_shared_memory_data(struct shmem_all *node_arr, int *total,
                            int *beep, int *ai, int *fan, int *temp,
                            int *humidity, int *light, int *person_detect, int *motor)
{     //还得修改这个地方9.20
    // 根据实际共享内存数据结构调整索引，这里假设：
    // 0: 蜂鸣器, 1: AI功能, 2: 排风扇, 3: 温度, 4: 湿度, 5: 灯, 6: 人员检测, 7: 马达
    if (*total > 0)
    {   //若采集成功，则赋予新数值，不成功则旧的数值
    //按照前三个的格式来    加
        *beep = node_arr[0].new_val.b_val ;
        *ai =  node_arr[1].new_val.b_val ;
        *fan =  node_arr[2].new_val.b_val ;
        *temp = (node_arr[3].ret == 0) ? node_arr[3].new_val.i_val : *temp;
        *humidity = (node_arr[4].ret == 0) ? node_arr[4].new_val.i_val : *humidity;
        *light = (node_arr[5].ret == 0) ? node_arr[5].new_val.b_val : *light;
        *person_detect = (node_arr[6].ret == 0) ? node_arr[6].new_val.b_val : *person_detect;
        *motor = (node_arr[7].ret == 0) ? node_arr[7].new_val.b_val : *motor;
        
        printf("Read shared memory data:\n");
        printf("  beep=%d, ai=%d, fan=%d, temp=%d, humidity=%d, light=%d, person_detect=%d, motor=%d\n",
               *beep, *ai, *fan, *temp, *humidity, *light, *person_detect, *motor);
    }
    else
    {
        printf("No data in shared memory\n");
    }
}

int main(int argc, char *argv[])
{
    MQTTClient client;
    MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
    int rc;
    char ch;

    // 初始化共享内存
    struct shm_param para;  
    int ret = shm_init(&para, "shm_txt", MAX_NODE_SIZE);
    if (ret < 0) {
        printf("共享内存初始化失败\n");
        return -1;
    }
    void *node_p = shm_getaddr(&para);
    if (node_p == NULL) {
        printf("获取共享内存地址失败\n");
        return -1;
    }
    
    int *total = (int *)node_p;                 // 节点总数
    struct shmem_all *node_arr = (struct shmem_all *)(node_p + sizeof(int)); // 节点数组
    
    printf("共享内存初始化完成，当前节点数: %d\n", *total);

    // 初始化变量（为了在华为云中显示，验证是否能够显示在华为云平台上）  这里也得加模块
    int beep = 0, ai = 0, fan = 0, temp = 0, 
        humidity = 0, light = 0, person_detect = 0, motor = 0;

    // 提取主机和端口信息
    char host[256];
    extract_host_from_address(BROKERADDRESS, host, sizeof(host));
    int port = extract_port_from_address(BROKERADDRESS);

    // 创建MQTT客户端
    printf("Creating MQTT client...\n");
    MQTTClient_create(&client, BROKERADDRESS, CLIENTID,
                      MQTTCLIENT_PERSISTENCE_NONE, NULL);

    // 设置回调函数
    MQTTClient_setCallbacks(client, NULL, connlost, msgarrvd, delivered);

    // 设置连接选项
    conn_opts.keepAliveInterval = KEEP_ALIVE_INTERVAL;
    conn_opts.cleansession = 1;
    conn_opts.username = USERNAME;
    conn_opts.password = PASSWORD;

    // 连接到MQTT broker
    printf("Connecting to MQTT broker...\n");
    printf("  Host: %s\n", host);
    printf("  Port: %d\n", port);
    
    if ((rc = MQTTClient_connect(client, &conn_opts)) != MQTTCLIENT_SUCCESS)
    {
        printf("Failed to connect, return code %d\n", rc);
        switch (rc)
        {
        case 1: printf("Error: Unacceptable protocol version\n"); break;
        case 2: printf("Error: Identifier rejected\n"); break;
        case 3: printf("Error: Server unavailable\n"); break;
        case 4: printf("Error: Bad username or password\n"); break;
        case 5: printf("Error: Not authorized\n"); break;
        default: printf("Error: Unknown connection error\n"); break;
        }
        MQTTClient_destroy(&client);
        exit(EXIT_FAILURE);
    }
    printf("Successfully connected to MQTT broker\n");

    // 显示连接信息
    print_connection_info(&conn_opts);

    // 订阅下行消息主题
    printf("Subscribing to topic: %s\n", SUBTOPIC);
    MQTTClient_subscribe(client, SUBTOPIC, QOS);

    // 设置终端模式，支持非阻塞按键检测
    set_terminal_mode();

    printf("\nSystem started. Monitoring shared memory and uploading data...\n");
    printf("  Data will be uploaded every %d seconds\n", UPLOAD_INTERVAL);
    printf("  Press 'q' to quit.\n");

    // 主循环：持续读取共享内存并上报数据
    while (1)
    {
        // 检查是否有退出请求
        if (kbhit())
        {
            ch = getchar();
            if (ch == 'q' || ch == 'Q')
            {
                break;
            }
        }

        // 读取共享内存中的最新数据  加模块
        read_shared_memory_data(node_arr, total, &beep, &ai, &fan, &temp,
                               &humidity, &light, &person_detect, &motor);

        // 发布数据到华为云   加模块
        printf("\nUploading data to Huawei Cloud...\n");
        rc = publish_json_message(client, PUBTOPIC, "id9", 
                                 beep, ai, fan, temp, humidity, 
                                 light, person_detect, motor);
        
        if (rc != MQTTCLIENT_SUCCESS)
        {
            printf("Failed to publish message, will retry in next interval\n");
        }

        // 等待指定时间，期间仍可检测退出按键
        printf("\nNext upload in %d seconds... (Press 'q' to quit)\n", UPLOAD_INTERVAL);
        for (int i = 0; i < UPLOAD_INTERVAL; i++)
        {
            sleep(1);
            if (kbhit() && (getchar() == 'q' || getchar() == 'Q'))
            {
                reset_terminal_mode();
                goto cleanup; // 跳转到资源清理
            }
        }
    }

cleanup:
    // 清理资源
    reset_terminal_mode();
    printf("Unsubscribing from topics...\n");
    MQTTClient_unsubscribe(client, SUBTOPIC);
    printf("Disconnecting from broker...\n");
    MQTTClient_disconnect(client, 10000);
    MQTTClient_destroy(&client);
    printf("Program stopped\n");

    return 0;
}
